/*
 * Copyright 2016 Victor Albertos
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package lib.jolyglot;

import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.Moshi;
import com.squareup.moshi.Types;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import lib.jolyglot.api.JolyglotGenerics;
import okio.BufferedSource;
import okio.Okio;

/**
 * Moshi implementation of Jolyglot
 */
public class MoshiSpeaker implements JolyglotGenerics {
  private final Moshi moshi;

  public MoshiSpeaker(Moshi moshi) {
    this.moshi = moshi;
  }

  public MoshiSpeaker() {
    this.moshi = new Moshi.Builder().build();
  }

  /** toJson
   * @param src the object for which Json representation is to be created.
   * @return String
   */
  @Override public String toJson(Object src)  {
    JsonAdapter<Object> jsonAdapter = moshi.adapter(Object.class);
    return jsonAdapter.toJson(src);
  }

  /** toJson
   * @param src the object for which JSON representation is to be created
   * @param typeOfSrc The specific genericized type of src.
   * @return String
   */
  @Override public String toJson(Object src, Type typeOfSrc) {
    JsonAdapter<Object> jsonAdapter = moshi.adapter(typeOfSrc);
    return jsonAdapter.toJson(src);
  }

  /** fromJson
   * @param json the string from which the object is to be deserialized
   * @param classOfT the class of T
   * @param <T>
   * @return T
   * @throws RuntimeException
   */
  @Override public <T> T fromJson(String json, Class<T> classOfT) throws RuntimeException {
    try {
      JsonAdapter<T> jsonAdapter = moshi.adapter(classOfT);
      return jsonAdapter.fromJson(json);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  /** fromJson
   * @param json
   * @param type
   * @param <T>
   * @return T
   * @throws RuntimeException
   */
  @Override public <T> T fromJson(String json, Type type) throws RuntimeException {
    try {
      JsonAdapter<T> jsonAdapter = moshi.adapter(type);
      return jsonAdapter.fromJson(json);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  /** fromJson
   * @param file the file producing the Json from which the object is to be deserialized.
   * @param classOfT the class of T
   * @param <T>
   * @return T
   * @throws RuntimeException
   */
  @Override public <T> T fromJson(File file, Class<T> classOfT) throws RuntimeException {
    BufferedSource bufferedSource = null;
    try {
      bufferedSource = Okio.buffer(Okio.source(file));
      JsonAdapter<T> jsonAdapter = moshi.adapter(classOfT);
      return jsonAdapter.fromJson(JsonReader.of(bufferedSource));
    } catch (IOException e) {
      throw new RuntimeException(e);
    } finally {
      if(bufferedSource != null){
        try {
          bufferedSource.close();
        } catch (IOException ignored) {
        }
      }
    }
  }

  /** fromJson
   * @param file the file producing Json from which the object is to be deserialized
   * @param typeOfT The specific genericized type of src.
   * @param <T>
   * @return T
   * @throws RuntimeException
   */
  @Override public <T> T fromJson(File file, Type typeOfT) throws RuntimeException {
    BufferedSource bufferedSource = null;
    try {
      bufferedSource = Okio.buffer(Okio.source(file));
      JsonAdapter<T> jsonAdapter = moshi.adapter(typeOfT);
      return jsonAdapter.fromJson(JsonReader.of(bufferedSource));
    } catch (IOException e) {
      throw new RuntimeException(e);
    } finally {
      if(bufferedSource != null){
        try {
          bufferedSource.close();
        } catch (IOException ignored) {
        }
      }
    }
  }

  /** arrayOf
   * @param componentType
   * @return GenericArrayType
   */
  @Override public GenericArrayType arrayOf(Type componentType) {
    return Types.arrayOf(componentType);
  }

  /** newParameterizedType
   * @param rawType
   * @param typeArguments
   * @return ParameterizedType
   */
  @Override public ParameterizedType newParameterizedType(Type rawType,
      Type... typeArguments) {
    return Types.newParameterizedType(rawType, typeArguments);
  }

}
